//
// The purpose of this project is to create a working, fun version of the board game Blokus.
// Using c++, DirectX and FMOD, I will use my own game engine for the purpose of making this game
// This project's progression and documentation will be monitored using TortoiseSVN source control
//
// GSP361 / Prof. Vaganov
//
// A project by Michael "Strog" Alstad


#define STRICT
#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <WindowsX.h>
#include <assert.h>

#include "resource.h"
#include "Graphics.h"
#include "DirectInput.h"
#include "DirectShow.h"
#include "Game.h"
#include "Sound.h"

// Win32 necessary function prototypes
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR psCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OpenWindow(const char* cszClassName, const char* cszWindowName, int nCmdShow);

// functions I've created for my own needs
bool Initialize(HINSTANCE hInstance, int nCmdShow);
bool Update(HINSTANCE hInstance, int nCmdShow);

// globals
HWND			hWnd		= NULL;			// handle to the window
Dimensions		g_dimScreen(800, 600);		// the width and height of the screen
eMainGameState	g_eState	= LOADING;		// set the main game state to LOADING

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR psCmdLine, int nCmdShow)
{
	MSG uMsg;
	memset(&uMsg, 0, sizeof(uMsg));
	while(uMsg.message != WM_QUIT && g_eState != QUIT)		// loop until either windows or the game engine is told to quit
	{
		while(PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE))	// translate and dispatch all messages in the message queue
		{
			TranslateMessage(&uMsg);
			DispatchMessage(&uMsg);
		}

		Update(hInstance, nCmdShow);										// updates all aspects of the game, returns false to signify program to quit
		DInput::Engine()->PollDevices();									// gets input from keyboard and mouse
		Graphics::Engine()->Render(Game::Engine()->getDrawables(),			// draw one frame
								   Game::Engine()->getTextBoxes());			
	}

	UnregisterClass("D3DWindow", hInstance);

	return uMsg.wParam;
}

// initializes all aspects of the game (call other init functions inside here)
bool Initialize(HINSTANCE hInstance, int nCmdShow)
{
	OpenWindow("D3DWindow", "Blokus Duo Digitus", nCmdShow);			// attempt to open a new window
	if(NULL == hWnd)													// if the window was not initialized properly, quit
		return false;

	RECT rClientRect;
	GetClientRect(hWnd, &rClientRect);									// obtain the actual window's dimensions
	g_dimScreen.width = (int)(rClientRect.right - rClientRect.left);
	g_dimScreen.height = (int)(rClientRect.bottom - rClientRect.top);
	Vector<char*> effectFiles;
	Vector<char*> streamFiles;
	
	effectFiles.push("Resource/Sound/selection.wav");
	effectFiles.push("Resource/Sound/select.wav");
	effectFiles.push("Resource/Sound/place.wav");
	effectFiles.push("Resource/Sound/move.wav");
	effectFiles.push("Resource/Sound/error.wav");

	streamFiles.push("Resource/Sound/stream.mp3");

	//////////////////// begin initialization of all the aspects of the game engine ////////////////////

	// if any of the initializations failed, return false, signifying that something went wrong
	if(!Graphics::Engine()->Init(hWnd))					
		return false;
	if(!DInput::Engine()->Init(hWnd, hInstance))			
		return false;
	if(!DirectShow::Engine()->Init(hWnd))
		return false;
	if(!Game::Engine()->Init())
		return false;
	if(!Sound::Engine()->Init(effectFiles, streamFiles))
		return false;
	
	//////////////////// end initialization of engines ////////////////////

	return true; // if everything initialized fine, return true
}

// determines what to update based on what the state of the game is
// returns true if the program is to continue, and false to quit
bool Update(HINSTANCE hInstance, int nCmdShow)
{
	switch(g_eState)
	{
	case INTRO:
		break;
	case LOADING:			// in the loading state, simply call the initialize function, and then continue on to the game
		if(Initialize(hInstance, nCmdShow))
			g_eState = GAME;
		else
			g_eState = QUIT;
		Sound::Engine()->Stream(0);	// turn on the jams

		break;
	case MENU:
		break;
	case GAME:
		if(DInput::Engine()->keyPressed(DIK_ESCAPE))
			g_eState = QUIT;
		Game::Engine()->Update();
		break;
	case QUIT:
		g_eState = QUIT;
	}

	// tell Graphics about all the pieces that have changed in Game
	Vector<Piece*> vUpdatedPieces = Game::Engine()->getPieces();
	int indexOffset = 0;

	// if there are updated pieces
	if(vUpdatedPieces.sizeOf() != 0)
	{
		for(int i = 0; i < vUpdatedPieces.sizeOf(); i++)
		{
			if(vUpdatedPieces[i]->owner == PLAYER2)
				indexOffset = numPieces;
			else
				indexOffset = 0;
			Graphics::Engine()->setPiece(vUpdatedPieces[i]->m_bppShape,
				vUpdatedPieces[i]->m_iType + indexOffset, 
				vUpdatedPieces[i]->getSize());
		}
	}
	vUpdatedPieces.release();

	// allow the sound engine to keep working
	Sound::Engine()->DoWork();

	return true;
}

void OpenWindow(const char* cszWinClassName, const char* cszWindowName, int nCmdShow)
{
	/* Register window class and create window */

	WNDCLASSEX winClass;

	HINSTANCE hInstance = ::GetModuleHandle(NULL);
	assert(NULL != hInstance);
	HICON hIcon = ::LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
	assert(NULL != hIcon);

	winClass.lpszClassName = cszWinClassName;
	winClass.cbSize        = sizeof(WNDCLASSEX);
	winClass.style         = CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc   = WindowProc;
	winClass.hInstance     = hInstance;
	winClass.hIcon		   = hIcon;
    winClass.hIconSm	   = hIcon;
	winClass.hCursor       = ::LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName  = NULL;
	winClass.cbClsExtra    = 0;
	winClass.cbWndExtra    = 0;

	if (0 == RegisterClassEx(&winClass))
	{
		return;
	}

	hWnd = ::CreateWindowEx(NULL,
							cszWinClassName,
							cszWindowName,
							WS_OVERLAPPEDWINDOW,
							0, 0,
							g_dimScreen.width, g_dimScreen.height,
							NULL, NULL, ::GetModuleHandle(NULL), NULL);

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
		case WM_CLOSE:
				PostQuitMessage(0);
				break;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
